package edit

import BaseViewModel
import LoadingStatus
import LocationsRepository
import ObjectsRepository
import admin.models.AdminObject
import admin.models.CreateComponent
import admin.models.CreateComputer
import android.util.Log
import androidx.lifecycle.viewModelScope
import checkNullAndGet
import edit.models.EditObjectsEvent
import edit.models.EditObjectsViewState
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import models.CreateComponentRequest
import models.CreateComputerRequest
import models.LocationUi
import my.models.ComputerUi

class EditObjectViewModel(
    private val repository: ObjectsRepository,
    private val locationsRepo: LocationsRepository
) : BaseViewModel<EditObjectsViewState, EditObjectsEvent>(
    initialState = EditObjectsViewState()
) {
    override fun obtainEvent(event: EditObjectsEvent) {
        when (event) {
            is EditObjectsEvent.SendObject -> sendObject()
            is EditObjectsEvent.SetId -> setId(event.id)
            is EditObjectsEvent.SetObject -> setObject(event.obj)
            is EditObjectsEvent.SetSerial -> {
                updateState {
                    it.copy(
                        obj = when (it.obj) {
                            is CreateComponent -> it.obj.copy(serialNumber = event.serial)
                            is CreateComputer -> it.obj.copy(serialNumber = event.serial)
                            else -> it.obj
                        }
                    )
                }
                Log.d("EditObjectViewModel", "obtainEvent: ${viewState.obj}")
            }
        }
    }

    private fun setId(id: Int) {
        updateState { it.copy(id = id) }
        viewModelScope.launch {
            repository.getComputer(id)
                .onSuccess { computer ->
                    setObject(
                        CreateComputer(
                            computer!!.serialNumber,
                            computer.location.name,
                            computer.location.id,
                            computer.model,
                            computer.manufacturer,
                            computer.purchaseDate,
                            computer.notes
                        )
                    )
                }
                .onFailure {
                    repository.getComponent(id)
                        .onSuccess { component ->
                            setObject(
                                CreateComponent(
                                    component!!.name,
                                    component.serialNumber,
                                    component.location.name,
                                    component.location.id,
                                    component.type,
                                    component.specifications,
                                    component.computerId,
                                    component.purchaseDate,
                                )
                            )
                        }
                        .onFailure { t ->
                            updateState { it.copy(loadingStatus = LoadingStatus.Error(t.message)) }
                        }

                }
        }
        Log.d("EditObjectViewModel", "setId: ${viewState.obj}")
    }

    private fun setObject(obj: AdminObject) {
        updateState { it.copy(obj = obj) }
        with(viewModelScope) {
            launch {
                locationsRepo.locations()
                    .onSuccess { locations ->
                        updateState { it.copy(locations = locations.map(LocationUi::from)) }
                    }
                    .onFailure { t ->
                        updateState { it.copy(loadingStatus = LoadingStatus.Error(t.message)) }
                    }
            }
            launch {
                repository.getComponentTypes()
                    .onSuccess { types ->
                        updateState { it.copy(componentTypes = types) }
                    }
                    .onFailure { t ->
                        updateState { it.copy(loadingStatus = LoadingStatus.Error(t.message)) }
                    }

            }
        }
        if (obj is CreateComponent) {
            viewModelScope.launch {
                repository.getComputers()
                    .onSuccess { computers ->
                        updateState { it.copy(computers = computers.map(ComputerUi::fromComputer)) }
                    }
                    .onFailure { t ->
                        updateState { it.copy(loadingStatus = LoadingStatus.Error(t.message)) }
                    }
            }
        }
        Log.d("EditObjectViewModel", "setObject: ${viewState.obj}")
    }

    private fun sendObject() {
        updateState { it.copy(loadingStatus = LoadingStatus.Loading) }
        Log.d("EditObjectViewModel", "sendObject: ${viewState.obj}")
        val obj = viewState.obj ?: return
        if (viewState.id != null) {
            editObject(viewState.id!!, obj)
            return
        }
        var job: Job? = null
        var component = obj
        if (obj.locationId == -1) {
            job = viewModelScope.launch {
                createLocation(obj.locationName)
                    .onSuccess {
                        component = when (obj) {
                            is CreateComponent -> obj.copy(locationId = it)
                            is CreateComputer -> obj.copy(locationId = it)
                            else -> {
                                obj
                            }
                        }
                    }
                    .onFailure { t ->
                        updateState { it.copy(loadingStatus = LoadingStatus.Error(t.message)) }
                        return@launch
                    }
            }
        }
        viewModelScope.launch {
            job?.join()
            when (obj) {
                is CreateComponent -> {
                    repository.createComponent(
                        CreateComponentRequest(
                            obj.name,
                            obj.type,
                            obj.specifications,
                            obj.purchaseDate,
                            obj.computerId,
                            component.locationId,
                            obj.serialNumber
                        )
                    )
                        .onSuccess {
                            updateState { it.copy(loadingStatus = LoadingStatus.FullyLoad) }
                        }
                        .onFailure { t ->
                            updateState { it.copy(loadingStatus = LoadingStatus.Error(t.message)) }
                        }
                }

                is CreateComputer -> {
                    repository.createComputer(
                        CreateComputerRequest(
                            obj.serialNumber,
                            obj.model,
                            obj.manufacturer,
                            obj.purchaseDate,
                            component.locationId,
                            obj.notes
                        )
                    )
                        .onSuccess {
                            updateState { it.copy(loadingStatus = LoadingStatus.FullyLoad) }
                        }
                        .onFailure { t ->
                            updateState { it.copy(loadingStatus = LoadingStatus.Error(t.message)) }
                        }
                }
            }
        }
        Log.d("EditObjectViewModel", "sendObject: ${viewState.obj}")
    }

    private fun editObject(id: Int, obj: AdminObject) {
        var job: Job? = null
        var component = obj
        if (obj.locationId == -1) {
            job = viewModelScope.launch {
                createLocation(obj.locationName)
                    .onSuccess {
                        component = when (obj) {
                            is CreateComponent -> obj.copy(locationId = it)
                            is CreateComputer -> obj.copy(locationId = it)
                            else -> {
                                obj
                            }
                        }
                    }
                    .onFailure { t ->
                        updateState { it.copy(loadingStatus = LoadingStatus.Error(t.message)) }
                        return@launch
                    }
            }
        }
        viewModelScope.launch {
            job?.join()
            when (obj) {
                is CreateComputer -> {
                    launch {
                        repository.updateComputer(
                            id,
                            CreateComputerRequest(
                                obj.serialNumber,
                                obj.model,
                                obj.manufacturer,
                                obj.purchaseDate,
                                component.locationId,
                                obj.notes

                            )
                        )
                            .onSuccess {
                                updateState {
                                    it.copy(loadingStatus = LoadingStatus.FullyLoad)
                                }
                            }
                            .onFailure { t ->
                                updateState { it.copy(loadingStatus = LoadingStatus.Error(t.message)) }
                            }
                    }
                }

                is CreateComponent -> {
                    launch {
                        repository.updateComponent(
                            id,
                            CreateComponentRequest(
                                obj.name,
                                obj.type,
                                obj.specifications,
                                obj.purchaseDate,
                                obj.computerId,
                                obj.locationId,
                                obj.serialNumber
                            )
                        )
                            .onSuccess {
                                updateState {
                                    it.copy(loadingStatus = LoadingStatus.FullyLoad)
                                }
                            }
                            .onFailure { t ->
                                updateState { it.copy(loadingStatus = LoadingStatus.Error(t.message)) }
                            }
                    }

                }
            }
        }
        Log.d("EditObjectViewModel", "editObject: ${viewState.obj}")
    }

    private suspend fun createLocation(name: String) =
        withContext(viewModelScope.coroutineContext) {
            locationsRepo.createLocation(name)
                .onFailure { t ->
                    updateState { it.copy(loadingStatus = LoadingStatus.Error(t.message)) }
                }
                .checkNullAndGet()
        }
}